package com.hup.minicontroller.common.bluetooth.handler;

import android.os.Handler;
import android.util.Log;

import com.hup.minicontroller.common.bluetooth.framework.BtBaseFunctionHandler;
import com.hup.minicontroller.common.bluetooth.framework.BtChannel;
import com.hup.minicontroller.common.bluetooth.model.BaseBtDataPacket;
import com.hup.minicontroller.common.bluetooth.model.BtHeartbeatPacket;

import java.io.IOException;
import java.util.Iterator;

import lombok.Getter;

/**
 * 心跳包处理器/模块;<pre>
 * 功能逻辑:
 * .连接建立后,(server和client各自都会)持续监听'有无收到远端socket发送数据',若 {@link #readerIdleTime}秒内无收到任何数据,则判断连接丢失
 * .对于发送心跳包 {@link BtHeartbeatPacket},则分为[主动发送方]和[被动回复方], 用 {@link #isActiveSender}进行配置
 * .[主动发送方]:连接建立后,定时主动发送 {@link BtHeartbeatPacket}
 * .[被动回复方]:只有在收到远端的 {@link BtHeartbeatPacket}后才回复 {@link BtHeartbeatPacket}
 * .至此就建立了通信双方的心跳机制
 * 使用方式:
 * .初始化 {@link BtChannel}时,创建本类实例,并添加到 {@link BtBaseFunctionHandler}列表中
 * 其他说明:
 * .使用 {@link BtChannel},每次读到数据时,都会被 {@link BtBaseFunctionHandler}处理一遍,实现[持续监听远端有无消息]
 * .使用 {@link ReaderIdleWatcher}实现[持续监听有无idle]
 * .使用 {@link HeartbeatActiveSender}实现[主动发送方],
 *
 * @author hugan
 * @date 2021/5/17
 */
public class BtHeartbeatHandler implements BtBaseFunctionHandler {
    private static final String TAG = BtHeartbeatHandler.class.getSimpleName();

    /**
     * n毫秒内无读取到数据,则判断为连接丢失
     */
    @Getter
    private static int readerIdleTime = 10 * 1000;
    /**
     * 心跳包发送间隔,小于readerIdleTime
     */
    private static long heartbeatInterval = (int) (readerIdleTime * 0.5);

    static {
        //注册HeartbeatPacket的编解码器
        BtDataSplitCodecHandler.INSTANCE.registerType(BtHeartbeatPacket.class);
    }

    private boolean isActiveSender;
    private BtChannel channel;
    private BtIdleCallback idleCallback;
    private Handler handler;
    private long lastReceiveTime;

    public BtHeartbeatHandler(boolean isActiveSender, BtChannel channel, BtIdleCallback idleCallback) {
        this.isActiveSender = isActiveSender;
        this.channel = channel;
        this.idleCallback = idleCallback;
        this.handler = idleCallback.getHandler();
        lastReceiveTime = System.currentTimeMillis();
        //添加监听器,监听有无ChannelIdle
        handler.postDelayed(new ReaderIdleWatcher(), readerIdleTime);
        if (isActiveSender) {
            //主动发送方
            handler.postDelayed(new HeartbeatActiveSender(), heartbeatInterval);
        }
    }

    @Override
    public BaseBtDataPacket onReceived(BtChannel btChannel, BaseBtDataPacket data, Iterator<BtBaseFunctionHandler> it) throws IOException {
        lastReceiveTime = System.currentTimeMillis();
        if (!(data instanceof BtHeartbeatPacket)) return data;//不是心跳数据,向后传递

        if (!isActiveSender) {
            //被动回复方
            Log.i(TAG, "onReceived: 收到并回复心跳包");
            btChannel.send(new BtHeartbeatPacket());
        }
        return null;//心跳数据,消费后返回null
    }

    /**
     * 监听有无ChannelIdle.
     * 参考自:io.netty.handler.timeout.IdleStateHandler.ReaderIdleTimeoutTask
     */
    private class ReaderIdleWatcher implements Runnable {

        @Override
        public void run() {
            if (channel.isClosed()) return;
            long cur = System.currentTimeMillis();
            long nextDelay = readerIdleTime - (cur - lastReceiveTime);
            if (nextDelay <= 0) {
                idleCallback.onChannelIdle(channel);
            } else {
                handler.postDelayed(this, nextDelay);
            }
        }
    }

    /**
     * 主动发送心跳包的Sender
     */
    private class HeartbeatActiveSender implements Runnable {

        @Override
        public void run() {
            if (channel.isClosed()) return;
            Log.i(TAG, "run: 发送心跳包");
            channel.send(new BtHeartbeatPacket());
            handler.postDelayed(this, heartbeatInterval);
        }
    }

    public interface BtIdleCallback {

        Handler getHandler();

        /**
         * n秒内无收到任何远端消息时的心跳机制回调
         */
        default void onChannelIdle(BtChannel btChannel) {
            Log.i(TAG, "onChannelIdle: 无收到消息=" + readerIdleTime + "秒,主动关闭连接");
            btChannel.disconnect();
        }
    }
}
